package com.matrix.rpc.server;

import com.matrix.rpc.client.RpcClient;
import com.matrix.rpc.common.RpcRequest;
import com.matrix.rpc.common.RpcResponse;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Map;


/**
 * 处理具体的业务调用
 * 通过构造时候传入的“业务接口和实现”handlerMap，来调用客户端请求的业务方法。
 * 并将业务方法返回值封装成response对象写入下一个handler（即编码handler--RpcEncoder）
 */
public class RpcHandler extends SimpleChannelInboundHandler<RpcRequest> {

    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(RpcClient.class);

    private final Map<String, Object> handlerMap;

    public RpcHandler(Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
    }

    @Override
    public void channelRead0(final ChannelHandlerContext ctx, RpcRequest request) throws Exception {
        RpcResponse response = new RpcResponse();
        response.setResponseId(request.getRequestId());
        try {
            //根据request来处理具体的业务逻辑。
            Object result = handle(request);
            response.setResult(result);
        } catch (Throwable throwable) {
            response.setError(throwable);
        }

        //写入outbundle(即RpcEncoder）进入下一步处理（即编码）后发送给channel客户端。
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    private Object handle(RpcRequest request) throws Throwable{
        String className = request.getClassName();
        //拿到实现类对象
        Object serviceBean = handlerMap.get(className);

        //拿到实现类的方法名，方法参数，参数值
        String methodName = request.getMethodName();
        Class<?>[] parameterTypes = request.getParameterTypes();
        Object[] parameters = request.getParameters();

        //拿到接口类
        Class<?> forName = Class.forName(className);

        Method method = forName.getMethod(methodName, parameterTypes);
        return method.invoke(serviceBean, parameters);

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.error("server caught exception", cause);
        ctx.close();
    }
}
